home *** CD-ROM | disk | FTP | other *** search
/ Turnbull China Bikeride / Turnbull China Bikeride - Disc 2.iso / BARNET / ARMTEX / EXTRAS / DRAW2LAT / Source / cc / draw2latex
Text File  |  1998-10-18  |  40KB  |  1,282 lines

  1. // -*- C++ -*-
  2. /* $Id: cc.draw2latex 1.8 1998/10/18 15:10:15 atterer Exp $
  3.   __   _
  4.   |_) /|  Copyright Richard Atterer
  5.   | \/¯|  <atterer@augsburg.baynet.de>
  6.   ¯ ´` ¯
  7.   Conversion of Draw files into LaTeX (picture env)
  8.  
  9.   This program is free software; you can redistribute it and/or modify
  10.   it under the terms of the GNU General Public License as published by
  11.   the Free Software Foundation; either version 2 of the License, or
  12.   (at your option) any later version.
  13.  
  14.   This program is distributed in the hope that it will be useful,
  15.   but WITHOUT ANY WARRANTY; without even the implied warranty of
  16.   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17.   GNU General Public License for more details.
  18.  
  19.   You should have received a copy of the GNU General Public License
  20.   along with this program; if not, write to the Free Software
  21.   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  22.   _________________________________________________________________________
  23.  
  24.   $Log: cc.draw2latex $
  25.   Revision 1.8  1998/10/18 15:10:15  atterer
  26.   command line switches; released as V1
  27.  
  28.   Revision 1.7  1998/10/17 11:15:27  atterer
  29.   extract comments from text areas, substitute '#1' to '#8'
  30.  
  31.   Revision 1.6  1998/09/08 21:10:42  atterer
  32.   arrowheads positioned correctly for diagonal lines
  33.   very basic circle support
  34.  
  35.   Revision 1.5  1998/09/05 00:40:10  atterer
  36.   arrowheads (as seperate \vector commands)
  37.  
  38.   Revision 1.4  1998/09/02 23:42:38  atterer
  39.   Text labels (with extra commands for Trinity/Homerton/Corpus as well as
  40.   Oblique/Bold), Grouped objects of a box and a text label turned into one
  41.   box with text centered
  42.  
  43.   Revision 1.3  1998/08/31 22:50:05  atterer
  44.   recognizes boxes
  45.  
  46.   Revision 1.2  1998/08/30 18:55:31  atterer
  47.   handling of short straight lines, output of numbers with supressed
  48.   zeroes after decimal point.
  49.  
  50.   Revision 1.1  1998/08/29 16:43:21  atterer
  51.   Initial revision
  52.  
  53. */
  54.  
  55. #include <kernel.h>
  56. #include <limits.h>
  57. #include <math.h>
  58. #include <stdio.h>
  59. #include <stdlib.h>
  60. #include <string.h>
  61. #include <std/typeinfo.h>
  62.  
  63. static const double pi = 3.141592654;
  64.  
  65. inline double dabs(double n) {
  66.   return (n > 0 ? n : -n);
  67. }
  68. inline int iabs(int n) {
  69.   return (n > 0 ? n : -n);
  70. }
  71.  
  72. extern const char* const rcsid =
  73. "$Id: cc.draw2latex 1.8 1998/10/18 15:10:15 atterer Exp $";
  74.  
  75. #ifdef DEBUG
  76.   #define D(cmd) cmd
  77.   #define ND(cmd)
  78.   #define log(args) printf args
  79. #else
  80.   #define D(cmd)
  81.   #define ND(cmd) cmd
  82.   #define log(args)
  83. #endif
  84. //______________________________________________________________________
  85.  
  86. // switches
  87. // true => turn short straight lines into beziers, false => make longer
  88. static bool shortbezline = false;
  89. static const double shortlinelen = 10;
  90. // true => (0,0) in {picture} is lower left of global bbox of drawfile,
  91. // false => (0,0) in {picture} is (0,0) in drawfile.
  92. static bool usedrawbbox = true;
  93. //____________________
  94.  
  95. typedef signed coord;
  96. typedef unsigned word;
  97.  
  98. struct XY {
  99.   coord x, y;
  100.   bool near(const XY& p) const {
  101.     return dabs(x - p.x) < 8 * 256
  102.            && dabs(y - p.y) < 8 * 256;
  103.   }
  104.   void avg(const XY& a, const XY& b) {
  105.     x = (a.x + b.x) / 2;
  106.     y = (a.y + b.y) / 2;
  107.   }
  108.   XY(void) { } // don't initialise
  109.   XY(coord cx, coord cy) : x(cx), y(cy) { }
  110. };
  111.  
  112. enum thinthick { neither, thin, thick };
  113. static int print_thinthick(FILE* file, thinthick newt);
  114. //________________________________________
  115.  
  116. struct BBox {
  117.   int load(FILE* file) {
  118.     return fread(&min.x, sizeof(coord), 4, file); // ahem, not very nice...
  119.   }
  120.   void set_from_mem(const void* ptr) {
  121.     *this = *(BBox*)ptr; // AHEM...
  122.   }
  123.   // alter bbox so that other box is included
  124.   void merge(const BBox* other) {
  125.     if (min.x > other->min.x) min.x = other->min.x;
  126.     if (min.y > other->min.y) min.y = other->min.y;
  127.     if (max.x < other->max.x) max.x = other->max.x;
  128.     if (max.y < other->max.y) max.y = other->max.y;
  129.   }
  130.   // min inclusive, max exclusive
  131.   XY min, max;
  132. };
  133. //________________________________________
  134.  
  135. // base class for draw objects
  136. class Drawfile;
  137.  
  138. class DrawObj {
  139. public:
  140.   DrawObj(Drawfile* p) : next(0), parent(p) { }
  141.   virtual ~DrawObj() { }
  142.   /* read object definition from file - file pointer points to word after
  143.      the object type field. returns zero for success */
  144.   virtual int load(FILE* file) = 0;
  145.   // output LaTeX
  146.   virtual int latex(FILE* file, const BBox& globbox) const = 0;
  147.   virtual thinthick thinstate(void) = 0;
  148.   void setnext(DrawObj* obj) { next = obj; return; }
  149.   DrawObj* getnext() const { return next; }
  150.   void setparent(Drawfile* f) { parent = f; return; }
  151.   Drawfile* getparent() const { return parent; }
  152.   const BBox* getbbox() const { return &bbox; }
  153. protected:
  154.   BBox bbox;
  155.   DrawObj* next;
  156.   Drawfile* parent;
  157. };
  158. //____________________
  159.  
  160. class Path : public DrawObj {
  161. public:
  162.   Path(Drawfile* p) : point(0), element(0), DrawObj(p) { }
  163.   int load(FILE* file);
  164.   int latex(FILE* file, const BBox& globbox) const;
  165.   thinthick thinstate(void) { return thinl; }
  166.   static void setlabel(const char* l1, const char* l2) {
  167.     label1 = const_cast<char*>(l1);
  168.     label2 = const_cast<char*>(l2);
  169.   }
  170.   static bool usedlabel() { return label1 == 0; }
  171.   ~Path() {
  172.     if (point != 0) free(point);
  173.     if (element != 0) free(element);
  174.   }
  175. private:
  176.   thinthick thinl;
  177.   bool sarrow, earrow; // arrowhead at start/end?
  178.   enum elemtype { move, close, draw, bezier };
  179.   struct tag {
  180.     elemtype type;
  181.     int firstpoint;
  182.   };
  183.  
  184.   static char* label1;
  185.   static char* label2;
  186.   int points; // nr of entries in point[]
  187.   XY* point;
  188.   int elements; // nr of entries in element[] and firstpoint[]
  189.   tag* element;
  190. };
  191. //____________________
  192.  
  193. class Label : public DrawObj {
  194. public:
  195.   Label(Drawfile* p) : text(0), DrawObj(p) { }
  196.   int load(FILE* file);
  197.   int latex(FILE* file, const BBox& globbox) const;
  198.   thinthick thinstate(void) { return neither; }
  199.   const char* fontcmd(void) const;
  200.   const char* getlabel() { return text; }
  201.   ~Label() { if (text) delete text; }
  202. private:
  203.   char* text;
  204.   int fontnr;
  205. };
  206. //____________________
  207.  
  208. class Group : public DrawObj {
  209. public:
  210.   Group(Drawfile* p) : first(0), DrawObj(p) { }
  211.   int load(FILE* file);
  212.   int latex(FILE* file, const BBox& globbox) const;
  213.   thinthick thinstate(void) { return neither; }
  214.   ~Group();
  215. private:
  216.   DrawObj* first;
  217. };
  218. //____________________
  219.  
  220. class Textarea : public DrawObj {
  221. public:
  222.   Textarea(Drawfile* p) : text(0), DrawObj(p) { }
  223.   int load(FILE* file);
  224.   int latex(FILE* file, const BBox& globbox) const;
  225.   thinthick thinstate(void) { return thin; }
  226.   ~Textarea();
  227. private:
  228.   char* text;
  229. };
  230. //________________________________________
  231.  
  232. enum fonttype { nofont = -1,
  233.   roman = 0, roman_i = 1, roman_b = 2, roman_ib = 3,
  234.   sanss = 4, sanss_i = 5, sanss_b = 6, sanss_ib = 7,
  235.   typew = 8, typew_i = 9, typew_b = 10, typew_ib = 11 };
  236.  
  237. class Drawfile {
  238. public:
  239.   // object type numbers as stored in Draw files
  240.   enum objtype {
  241.     type_fonttable = 0,
  242.     type_label = 1, // Text object
  243.     type_path = 2,
  244.     type_group = 6,
  245.     type_textarea = 9,
  246.     type_transformlabel = 12 };
  247.   // set up Drawfile data structure from file
  248.   int load(FILE* file);
  249.   // output in LaTeX format, using {picture}
  250.   int latex(FILE* file) const;
  251.   // create bbox data from that of all objects in the Drawfile
  252.   void make_bbox(void);
  253.   static int latex_line(FILE* file, coord x0, coord y0, coord x1, coord y1);
  254.   static coord latex_lineerrx(void) { return lineerr.x; }
  255.   static coord latex_lineerry(void) { return lineerr.y; }
  256.   static int latex_arrowhead(FILE* file, coord x, coord y, double angle);
  257.   ~Drawfile();
  258.   static int loadobj(FILE* file, DrawObj*& dest, Drawfile* destfile);
  259.   int loadfonttable(FILE* file);
  260.   fonttype fontentry(int idx) { return fonttable[idx]; }
  261. private:
  262.   static inline void Drawfile::cmp_angle(double& min, double angle,
  263.                      double& best, signed& bestoutx, signed& bestouty,
  264.                      signed outx, signed outy, double outangle);
  265.   static XY lineerr;
  266.   fonttype fonttable[256];
  267.   BBox bbox;
  268.   DrawObj* first;
  269. };
  270. //______________________________________________________________________
  271. //______________________________________________________________________
  272.  
  273. Drawfile::~Drawfile(void)
  274. {
  275.   DrawObj* o = first;
  276.   while (o) {
  277.     DrawObj* n = o->getnext();
  278.     log(("~Drawfile: deleting %x, next at %x\n", (int)o, (int)n));
  279.     delete o;
  280.     o = n;
  281.   }
  282.   return;
  283. }
  284. //________________________________________
  285.  
  286. int Drawfile::loadfonttable(FILE* file)
  287. {
  288.   word size;
  289.   long pos = ftell(file);
  290.   if (pos == -1L) return 1;
  291.   if (fread(&size, sizeof(word), 1, file) == 0) return 1;
  292.   pos += size - sizeof(word);
  293.  
  294.   // load font names until size reached
  295.   char name[512];
  296.   long currpos;
  297.   while ((currpos = ftell(file)) != -1L && currpos < pos - 3
  298.          && feof(file) == 0) {
  299.     int idx = getc(file);
  300.     if (idx == EOF) return 1;
  301.     int c;
  302.     char* nameptr = name;
  303.     while ((c = getc(file)) >= ' ') *nameptr++ = c;
  304.     *nameptr = 0;
  305.     log(("Drawfile::loadfonttable: pos=%ld, currpos=%ld, #%d=\"%s\"\n", pos, currpos, idx, name));
  306.  
  307.     if (strncmp(name, "Trinity", 7) == 0)
  308.       fonttable[idx] = roman;
  309.     else if (strncmp(name, "Homerton", 8) == 0)
  310.       fonttable[idx] = sanss;
  311.     else if (strncmp(name, "Corpus", 6) == 0)
  312.       fonttable[idx] = typew;
  313.     if (fonttable[idx] != nofont) {
  314.       if (strstr(name, ".Italic") || strstr(name, ".Oblique"))
  315.         fonttable[idx] = (fonttype)(int(fonttable[idx]) | 1);
  316.       if (strstr(name, ".Bold"))
  317.         fonttable[idx] = (fonttype)(int(fonttable[idx]) | 2);
  318.     }
  319.   }
  320.   log(("Drawfile::loadfonttable: end, pos=%ld, currpos=%ld\n", pos, currpos));
  321.   if (fseek(file, pos, SEEK_SET)) return 1; else return 0;
  322. }
  323. //________________________________________
  324.  
  325. int Drawfile::loadobj(FILE* file, DrawObj*& dest, Drawfile* destfile)
  326. {
  327.   log(("Drawfile::loadobj\n"));
  328.   // get one word of object type
  329.   bool read = false; // may have to skip unknown objects
  330.   while (!read) {
  331.     word objtypeword;
  332.     if (fread(&objtypeword, sizeof(word), 1, file) == 0) return 2;
  333.     //if (feof(file)) return 2;
  334.  
  335.     // read object
  336.     objtype curobjtype = (objtype)objtypeword;
  337.     log(("Drawfile::loadobj: objtype %d\n", curobjtype));
  338.     switch (curobjtype) {
  339.     case type_fonttable:
  340.       if (destfile == 0 || destfile->loadfonttable(file)) return 1;
  341.       break;
  342.     case type_label: {
  343.         dest = new Label(destfile);
  344.         if (dest->load(file)) { delete dest; dest = 0; return 1; }
  345.         read = true;
  346.         break;
  347.       }
  348.     case type_path: {
  349.         dest = new Path(destfile);
  350.         if (dest->load(file)) { delete dest; dest = 0; return 1; }
  351.         read = true;
  352.         break;
  353.       }
  354.     case type_group: {
  355.         dest = new Group(destfile);
  356.         if (dest->load(file)) { delete dest; dest = 0; return 1; }
  357.         read = true;
  358.         break;
  359.       }
  360.     case type_textarea: {
  361.         dest = new Textarea(destfile);
  362.         if (dest->load(file)) { delete dest; dest = 0; return 1; }
  363.         read = true;
  364.         break;
  365.       }
  366.     default:
  367.       // unknown object type; skip object
  368.       word len;
  369.       if (fread(&len, sizeof(word), 1, file) == 0) {
  370.         dest = 0;
  371.         return 1;
  372.       }
  373.       log(("Drawfile::load: unknown obj %d, len %d\n", curobjtype, len));
  374.       if (fseek(file, len - 2 * sizeof(word), SEEK_CUR)) {
  375.         dest = 0;
  376.         return 1;
  377.       }
  378.       break;
  379.     }
  380.   }
  381.   return 0;
  382. }
  383. //________________________________________
  384.  
  385. int Drawfile::load(FILE* file)
  386. {
  387.   log(("Drawfile::load\n"));
  388.  
  389.   int i = 255;
  390.   do {
  391.     fonttable[i] = nofont;
  392.   } while (--i >= 0);
  393.  
  394.   // read file header
  395.   word hdrword;
  396.   log(("Drawfile::load to %x\n", (int)&hdrword));
  397.   if (fread(&hdrword, sizeof(word), 1, file) == 0) return 1;
  398.   if (hdrword != 0x77617244) return 1; // 'Draw'
  399.   if (fseek(file, 40, SEEK_SET)) return 1;
  400.   //____________________
  401.  
  402.   // read objects
  403.   if (loadobj(file, first, this)) return 1;
  404.   DrawObj* prev = first;
  405.   while (feof(file) == 0) {
  406.     DrawObj* currobj;
  407.     int err;
  408.     if ((err = loadobj(file, currobj, this)) == 2) return 0;
  409.     if (err) return 1;
  410.     prev->setnext(currobj);
  411.     prev = currobj;
  412.   }
  413.   return 0;
  414. }
  415. //______________________________________________________________________
  416.  
  417. void Drawfile::make_bbox(void)
  418. {
  419.   DrawObj* o = first;
  420.   log(("Drawfile::make_bbox: first at %x\n", (int)o));
  421.   if (o) {
  422.     bbox = *o->getbbox();
  423.     o = o->getnext();
  424.   }
  425.   while (o) {
  426.     log(("Drawfile::make_bbox: merging with %x\n", (int)o));
  427.     bbox.merge(o->getbbox());
  428.     o = o->getnext();
  429.   }
  430.  
  431.   if (!usedrawbbox) {
  432.     bbox.max.x -= bbox.min.x;
  433.     bbox.max.y -= bbox.min.y;
  434.     bbox.min.x = 0; bbox.min.y = 0;
  435.     return;
  436.   }
  437.   return;
  438. }
  439. //______________________________________________________________________
  440.  
  441. char* Path::label1 = 0;
  442. char* Path::label2 = 0;
  443.  
  444. // load individual objects' data
  445. int Path::load(FILE* file)
  446. {
  447.   struct {
  448.     word size;
  449.     coord xmin, ymin, xmax, ymax;
  450.     word fillcol, outlinecol, outlinewidth;
  451.     word pathstyle;
  452.   } head;
  453.  
  454.   // read obj header
  455.   if (fread(&head, sizeof(head), 1, file) == 0) return 1;
  456.   bbox.set_from_mem(&head.xmin);
  457.  
  458.   if (head.outlinewidth) thinl = thick; else thinl = thin;
  459.   if ((head.pathstyle & 0x30) == 0x30) sarrow = true; else sarrow = false;
  460.   if ((head.pathstyle & 0xc) == 0xc) earrow = true; else earrow = false;
  461.  
  462.   if (head.pathstyle & 1<<7) { // skip dash pattern if present
  463.     struct { word offset, elements; } dashstart;
  464.     if (fread(&dashstart, sizeof(dashstart), 1, file) == 0) return 1;
  465.     log(("Path::load: skipping %d dash patterns\n", dashstart.elements));
  466.     if (fseek(file, dashstart.elements * sizeof(word), SEEK_CUR)) return 1;
  467.   }
  468.  
  469.   point = static_cast<XY*>(malloc(0)); // coordinates
  470.   if (point == 0) return 1;
  471.   points = 0;
  472.   element = static_cast<tag*>(malloc(0)); // tag info
  473.   if (element == 0) return 1;
  474.   elements = 0;
  475.  
  476.   // read path elements
  477.   word tagtype;
  478.   if (fread(&tagtype, sizeof(tagtype), 1, file) == 0) return 1;
  479.   while ((tagtype &= 0xff) != 0) { // until "end of path"
  480.     int coopairs; // nr of x/y pairs following
  481.     elemtype thistype;
  482.     switch (tagtype) {
  483.     case 2: coopairs = 1; thistype = move; break;
  484.     case 5: coopairs = 0; thistype = close; break;
  485.     case 8: coopairs = 1; thistype = draw; break;
  486.     case 6: coopairs = 3; thistype = bezier; break;
  487.     default: // just to avoid compiler warnings
  488.       coopairs = 0; thistype = (elemtype)0;
  489.       return 1;
  490.     }
  491.     log(("Path::load: tag type %d; %d coords follow\n", tagtype, coopairs));
  492.  
  493.     // enlarge element[]
  494.     elements++;
  495.     tag* e = (tag*)realloc(element, elements * sizeof(tag));
  496.     if (e == 0) return 1;
  497.     element = e;
  498.     element[elements - 1].type = thistype;
  499.     element[elements - 1].firstpoint = points;
  500.  
  501.     // enlarge point[]
  502.     if (coopairs) {
  503.       points += coopairs;
  504.       XY* p = (XY*)realloc(point, points * sizeof(XY));
  505.       if (p == 0) return 1;
  506.       point = p;
  507.       if (fread(&point[points - coopairs], coopairs * sizeof(XY),
  508.                 1, file) == 0) return 1;
  509.     }
  510.     if (fread(&tagtype, sizeof(tagtype), 1, file) == 0) return 1;
  511.   }
  512.   log(("Path::load: returning; %d elements, %d points\n", elements, points));
  513.   return 0;
  514. }
  515. //________________________________________
  516.  
  517. int Label::load(FILE* file)
  518. {
  519.   struct {
  520.     word size;
  521.     coord xmin, ymin, xmax, ymax;
  522.     word textcol, backcol, style, xsize, ysize, xbaseline, ybaseline;
  523.   } head;
  524.   log(("Label::load\n"));
  525.  
  526.   if (fread(&head, sizeof(head), 1, file) == 0) return 1;
  527.   head.xmin = head.xbaseline; head.ymin = head.ybaseline;
  528.   bbox.set_from_mem(&head.xmin);
  529.   int strlen = head.size - sizeof(head) - sizeof(word);
  530.   text = new char[strlen];
  531.   fontnr = head.style & 0xff;
  532.   if (text == 0) return 1;
  533.   if (fread(text, sizeof(char), strlen, file) == 0) return 1;
  534.   return 0;
  535. }
  536. //________________________________________
  537.  
  538. Group::~Group(void)
  539. {
  540.   DrawObj* o = first;
  541.   while (o) {
  542.     DrawObj* n = o->getnext();
  543.     log(("~Group: deleting %x, next at %x\n", (int)o, (int)n));
  544.     delete o;
  545.     o = n;
  546.   }
  547.   return;
  548. }
  549. //________________________________________
  550.  
  551. int Group::load(FILE* file)
  552. {
  553.   struct {
  554.     word size;
  555.     coord xmin, ymin, xmax, ymax;
  556.     char description[12];
  557.   } head;
  558.   log(("Group::load\n"));
  559.  
  560.   long pos = ftell(file);
  561.   if (pos == -1L) return 1;
  562.  
  563.   // read obj header
  564.   if (fread(&head, sizeof(head), 1, file) == 0) return 1;
  565.   bbox.set_from_mem(&head.xmin);
  566.   pos += head.size - sizeof(word);
  567.  
  568.   // load objects until size reached
  569.   if (Drawfile::loadobj(file, first, parent)) return 1;
  570.   DrawObj* prev = first;
  571.   long currpos;
  572.   while ((currpos = ftell(file)) != -1L && currpos < pos
  573.          && feof(file) == 0) {
  574.     log(("Group::load: pos=%ld, currpos=%ld\n", pos, currpos));
  575.     DrawObj* currobj;
  576.     int err;
  577.     if ((err = Drawfile::loadobj(file, currobj, parent)) == 2) return 0;
  578.     log(("Group::load: obj loaded to %x, prev at %x\n", (int)currobj, (int)prev));
  579.     if (err) return 1;
  580.     prev->setnext(currobj);
  581.     prev = currobj;
  582.   }
  583.   if (currpos >= pos) return 0; else return 1;
  584. }
  585. //________________________________________
  586.  
  587. Textarea::~Textarea(void)
  588. {
  589.   if (text) free(text);
  590.   return;
  591. }
  592. //________________________________________
  593.  
  594. int Textarea::load(FILE* file)
  595. {
  596.   struct {
  597.     word size;
  598.     coord xmin, ymin, xmax, ymax;
  599.     word dummy;
  600.   } head;
  601.   struct {
  602.     word size;
  603.     coord xmin, ymin, xmax, ymax;
  604.     word objtype; // objtype of following text column obj, or 0
  605.   } areaobj;
  606.   log(("Textarea::load\n"));
  607.  
  608.   // read obj header
  609.   if (fread(&head, sizeof(head), 1, file) == 0) return 1;
  610.   bbox.set_from_mem(&head.xmin);
  611.   // 4 of the 5 words are skipped below, 1 is for obj type
  612.   head.size -= sizeof(head) + 5 * sizeof(word);
  613.  
  614.   // skip text column objects
  615.   do {
  616.     if (fread(&areaobj, sizeof(areaobj), 1, file) == 0) return 1;
  617.     log(("Textarea::load: skip column\n"));
  618.     head.size -= sizeof(areaobj);
  619.   } while (areaobj.objtype != 0);
  620.  
  621.   // skip other words in header
  622.   if (fread(&areaobj, 4 * sizeof(word), 1, file) == 0) return 1;
  623.  
  624.   // now load ASCII textarea source
  625.   text = static_cast<char*>(malloc(head.size));
  626.   if (text == 0 || fread(text, head.size, 1, file) == 0) return 1;
  627.   log(("Textarea::load: size %d\n", head.size));
  628.   return 0;
  629. }
  630. //______________________________________________________________________
  631.  
  632. // convert to LaTeX coordinates
  633. int latex_coord(int len) {
  634.   return len * 72 / 180 / 256;
  635.   /* NB slightly incorrect; one TeX pt != 1/72"
  636.      72.27 TeX pt make one inch */
  637. }
  638.  
  639. double latex_coord(double len) {
  640.   return len * 72 / 180 / 256;
  641. }
  642.  
  643. double latex_coord1(double len) {
  644.   double n = double(int(len * 72 / 180 / 256 * 10 + .5)) / 10;
  645.   if (n > .01) return n; else return 0;
  646. }
  647.  
  648. // convert to Draw coordinates
  649. int coord_latex(double len) {
  650.   return int(len / 72 * 180 * 256);
  651. }
  652.  
  653. double round1(double n) {
  654.   double m = double(int(n * 10 + .5)) / 10;
  655.   if (m > .01) return m; else return 0;
  656. }
  657. double round2(double n) {
  658.   double m = double(int(n * 100 +.5)) / 100;
  659.   if (m > .001) return m; else return 0;
  660. }
  661. //________________________________________
  662.  
  663. // print "\thinlines" or "\thicklines"
  664. int print_thinthick(FILE* file, thinthick newt)
  665. {
  666.   static thinthick thinlines = neither;
  667.  
  668.   if (file == 0) {
  669.     thinlines = newt;
  670.     return 0;
  671.   }
  672.  
  673.   if (thinlines != newt) {
  674.     switch (newt) {
  675.     case neither:
  676.       break;
  677.     case thin:
  678.       if (fprintf(file, "\n\\thinlines") == 0) return 1;
  679.       thinlines = newt;
  680.       break;
  681.     case thick:
  682.       if (fprintf(file, "\n\\thicklines") == 0) return 1;
  683.       thinlines = newt;
  684.       break;
  685.     }
  686.   }
  687.   return 0;
  688. }
  689. //________________________________________
  690.  
  691. // output LaTeX
  692. int Drawfile::latex(FILE* file) const
  693. {
  694.   log(("Drawfile::latex\n"));
  695.   print_thinthick(0, neither);
  696.   if (fprintf(file, "\\documentclass[12pt]{article}\n\\begin{document}\n")
  697.       == 0) return 1;
  698.   if (fprintf(file, "\n%% converted by draw2latex"
  699.               "\n\\begin{picture}(%d,%d)",
  700.               latex_coord(bbox.max.x - bbox.min.x),
  701.               latex_coord(bbox.max.y - bbox.min.y))
  702.       == 0) return 1;
  703.   #if 0
  704.   if (fprintf(file, "\n\\put(0,0){\\framebox(%d,%d){}}",
  705.               latex_coord(bbox.max.x - bbox.min.x),
  706.               latex_coord(bbox.max.y - bbox.min.y))
  707.       == 0) return 1;
  708.   #endif
  709.   DrawObj* o = first;
  710.  
  711.   // output objects
  712.   while (o) {
  713.     thinthick newthinlines = o->thinstate();
  714.     if (print_thinthick(file, newthinlines)) return 1;
  715.     if (o->latex(file, bbox)) return 1;
  716.     o = o->getnext();
  717.   }
  718.  
  719.   if (print_thinthick(file, thin)) return 1;
  720.   if (fprintf(file, "\n\\end{picture}\n\n\\end{document}\n")
  721.       == 0) return 1;
  722.  
  723.   return 0;
  724. }
  725. //________________________________________
  726.  
  727. inline void Drawfile::cmp_angle(double& min, double angle, double& best,
  728.                      signed& bestoutx, signed& bestouty,
  729.                      signed outx, signed outy, double outangle) {
  730.   double tmp;
  731.   if (min > (tmp = dabs(angle - outangle))) {
  732.     bestoutx = outx; bestouty = outy;
  733.     min = tmp;
  734.     best = outangle;
  735.   }
  736. }
  737. //____________________
  738.  
  739. /* output a \vector{} command, but with a vector length of 0.
  740.    angle is where the vector points to, i.e. 0 is right, pi/2 is up etc.
  741.    angle must be from -pi to +pi */
  742. int Drawfile::latex_arrowhead(FILE* file, coord x, coord y, double angle)
  743. {
  744.   bool downward, left;
  745.  
  746.   double lx = latex_coord(double(x));
  747.   double ly = latex_coord(double(y));
  748.   lx += 3.5 * cos(angle); // don't let line overstrike tip of arrowhead
  749.   ly += 3.5 * sin(angle);
  750.   log(("Drawfile::latex_arrowhead: angle %f (%g,%g)\n", angle, lx, ly));
  751.  
  752.   // restrict angle to 0..90 degrees
  753.   if (angle < 0.0) {
  754.     downward = true;
  755.     angle = -angle;
  756.   } else {
  757.     downward = false;
  758.   }
  759.   if (angle > pi / 2) {
  760.     left = true;
  761.     angle = pi - angle;
  762.   } else {
  763.     left = false;
  764.   }
  765.  
  766.   signed bestoutx = 1, bestouty = 0;
  767.   double min = angle; // difference between angle and best angle so far
  768.   double best = 0; // best angle so far
  769.  
  770.   /* update best, bestoutx, outy if outangle nearer to angle than for
  771.      previous calls. outx,y is vector passed to \line{} */
  772.   cmp_angle(min, angle, best, bestoutx, bestouty, 1, 4, 1.32581766);
  773.   cmp_angle(min, angle, best, bestoutx, bestouty, 4, 1, 0.244978663);
  774.   cmp_angle(min, angle, best, bestoutx, bestouty, 3, 4, 0.927295218);
  775.   cmp_angle(min, angle, best, bestoutx, bestouty, 4, 3, 0.643501109);
  776.   cmp_angle(min, angle, best, bestoutx, bestouty, 1, 3, 1.24904577);
  777.   cmp_angle(min, angle, best, bestoutx, bestouty, 3, 1, 0.321750554);
  778.   cmp_angle(min, angle, best, bestoutx, bestouty, 2, 3, 0.982793723);
  779.   cmp_angle(min, angle, best, bestoutx, bestouty, 3, 2, 0.588002604);
  780.   cmp_angle(min, angle, best, bestoutx, bestouty, 1, 2, 1.10714872);
  781.   cmp_angle(min, angle, best, bestoutx, bestouty, 2, 1, 0.463647609);
  782.   cmp_angle(min, angle, best, bestoutx, bestouty, 1, 1, 0.785398163);
  783.   cmp_angle(min, angle, best, bestoutx, bestouty, 1, 1, 0.785398163);
  784.   cmp_angle(min, angle, best, bestoutx, bestouty, 0, 1, pi / 2);
  785.  
  786.   if (left) { bestoutx = -bestoutx; best = pi - best; }
  787.   if (downward) { bestouty = -bestouty; best = -best; }
  788.  
  789.   if (fprintf(file, "\n\\put(%.4g,%.4g){\\vector(%d,%d){0}}",
  790.               round1(lx), round1(ly), bestoutx, bestouty) == 0) return 1;
  791.   return 0;
  792. }
  793. //____________________
  794.  
  795. XY Drawfile::lineerr;
  796.  
  797. int Drawfile::latex_line(FILE* file, coord x0, coord y0, coord x1, coord y1)
  798. {
  799.   double dx = latex_coord(double(x0 - x1)); // distance
  800.   double dy = latex_coord(double(y0 - y1));
  801.  
  802.   // find best angle
  803.   double angle = dabs(atan2(dy, dx));
  804.   if (angle > pi / 2) angle = pi - angle; // angle 0..90 degrees
  805.   signed bestoutx = 1, bestouty = 0;
  806.   double min = angle; // difference between angle and best angle so far
  807.   double best = 0; // best angle so far
  808.  
  809.   /* update best, bestoutx, outy if outangle nearer to angle than for
  810.      previous calls. outx,y is vector passed to \line{} */
  811.   cmp_angle(min, angle, best, bestoutx, bestouty, 1, 6, 1.40564765);
  812.   cmp_angle(min, angle, best, bestoutx, bestouty, 6, 1, 0.165148677);
  813.   cmp_angle(min, angle, best, bestoutx, bestouty, 5, 6, 0.87605805);
  814.   cmp_angle(min, angle, best, bestoutx, bestouty, 6, 5, 0.694738276);
  815.   cmp_angle(min, angle, best, bestoutx, bestouty, 1, 5, 1.37340077);
  816.   cmp_angle(min, angle, best, bestoutx, bestouty, 5, 1, 0.19739556);
  817.   cmp_angle(min, angle, best, bestoutx, bestouty, 2, 5, 1.19028995);
  818.   cmp_angle(min, angle, best, bestoutx, bestouty, 5, 2, 0.380506377);
  819.   cmp_angle(min, angle, best, bestoutx, bestouty, 3, 5, 1.03037683);
  820.   cmp_angle(min, angle, best, bestoutx, bestouty, 5, 3, 0.5404195);
  821.   cmp_angle(min, angle, best, bestoutx, bestouty, 4, 5, 0.896055384);
  822.   cmp_angle(min, angle, best, bestoutx, bestouty, 5, 4, 0.674740942);
  823.   cmp_angle(min, angle, best, bestoutx, bestouty, 1, 4, 1.32581766);
  824.   cmp_angle(min, angle, best, bestoutx, bestouty, 4, 1, 0.244978663);
  825.   cmp_angle(min, angle, best, bestoutx, bestouty, 3, 4, 0.927295218);
  826.   cmp_angle(min, angle, best, bestoutx, bestouty, 4, 3, 0.643501109);
  827.   cmp_angle(min, angle, best, bestoutx, bestouty, 1, 3, 1.24904577);
  828.   cmp_angle(min, angle, best, bestoutx, bestouty, 3, 1, 0.321750554);
  829.   cmp_angle(min, angle, best, bestoutx, bestouty, 2, 3, 0.982793723);
  830.   cmp_angle(min, angle, best, bestoutx, bestouty, 3, 2, 0.588002604);
  831.   cmp_angle(min, angle, best, bestoutx, bestouty, 1, 2, 1.10714872);
  832.   cmp_angle(min, angle, best, bestoutx, bestouty, 2, 1, 0.463647609);
  833.   cmp_angle(min, angle, best, bestoutx, bestouty, 1, 1, 0.785398163);
  834.   cmp_angle(min, angle, best, bestoutx, bestouty, 1, 1, 0.785398163);
  835.   cmp_angle(min, angle, best, bestoutx, bestouty, 0, 1, pi / 2);
  836.  
  837.   // is line too short for TeX?
  838.   double len = sqrt(dx * dx + dy * dy);
  839.   if (len < 2) {
  840.     return 0; // ignore very short lines
  841.   }
  842.   if (bestoutx != 0 && bestouty != 0) {
  843.     double minlen = -1;
  844.     if (bestoutx < bestouty)
  845.       minlen = double(bestoutx) / double(bestouty);
  846.     else
  847.       minlen = double(bestouty) / double(bestoutx);
  848.     minlen = shortlinelen * sqrt(1.0 + minlen * minlen);
  849.     if (len < minlen) {
  850.       // too short
  851.       if (shortbezline) {
  852.         // either turn into bezier
  853.         lineerr.x = 0; lineerr.y = 0;
  854.         if (fprintf(file, "\n\\qbezier(%.4g,%.4g)(%.4g,%.4g)(%.4g,%.4g)",
  855.                     latex_coord1(double(x0)), latex_coord1(double(y0)),
  856.                     latex_coord1(double((x0 + x1) / 2)),
  857.                     latex_coord1(double((y0 + y1) / 2)),
  858.                     latex_coord1(double(x1)), latex_coord1(double(y1)))
  859.             == 0) return 1;
  860.         return 0;
  861.       } else {
  862.         // or make line longer
  863.         len = minlen;
  864.       }
  865.     }
  866.   }
  867.  
  868.   if (dx < 0) { bestoutx = -bestoutx; best = pi - best; }
  869.   if (dy < 0) { bestouty = -bestouty; best = -best; }
  870.  
  871.   // reposition line so error is minimal
  872.   double mx = latex_coord(double((x0 + x1) / 2)); // middle
  873.   double my = latex_coord(double((y0 + y1) / 2));
  874.   double wid = cos(best) * len;
  875.   mx -= wid / 2;
  876.   wid = dabs(wid);
  877.   my -= sin(best) * len / 2;
  878.  
  879.   lineerr.x = coord_latex(mx) - x1;
  880.   lineerr.y = coord_latex(my) - y1;
  881.  
  882.   if (bestoutx == 0) {
  883.     if (fprintf(file, "\n\\put(%.4g,%.4g){\\line(0,%d){%.4g}}",
  884.                 round1(mx), round1(my), bestouty, round1(len))
  885.         == 0) return 1;
  886.   } else {
  887.     if (fprintf(file, "\n\\put(%.4g,%.4g){\\line(%d,%d){%.5g}}",
  888.                 round1(mx), round1(my), bestoutx, bestouty, round2(wid))
  889.         == 0) return 1;
  890.   }
  891.   /*if (fprintf(file, "\n\\put(%.4g,%.4g){\\circle*{3}}",
  892.               (latex_coord(double(current.x))),
  893.               (latex_coord(double(current.y)))) == 0) return 1;*/
  894.   return 0;
  895. }
  896. //________________________________________
  897.  
  898. int Path::latex(FILE* file, const BBox& globbox) const
  899. {
  900.   XY current;
  901.  
  902.   // is path a box?
  903.   if (elements == 6 && points == 5 && element[0].type == move
  904.       && element[1].type == draw && element[2].type == draw
  905.       && element[3].type == draw && element[4].type == draw
  906.       && element[5].type == close && point[0].near(point[4])) {
  907.     log(("Path::latex: is box?\n"));
  908.     bool hor; // true => next segment must be horizontal, otherwise vertical
  909.     if (iabs(point[1].x - point[0].x) > iabs(point[1].y - point[0].y))
  910.       hor = true;
  911.     else
  912.       hor = false;
  913.  
  914.     bool valid = true;
  915.     int width = 0, height = 0, midx = 0, midy = 0;
  916.     int i = 0;
  917.     do { // horizontal and vertical lines must alternate
  918.       int dx = iabs(point[i+1].x - point[i].x);
  919.       int dy = iabs(point[i+1].y - point[i].y);
  920.       midx += point[i].x / 4; midy += point[i].y / 4;
  921.       if (hor) {
  922.         width += dx / 2;
  923.       } else {
  924.         height += dy / 2;
  925.         int tmp = dx; dx = dy; dy = tmp;
  926.       }
  927.       if (dy > dx / 12) valid = false;
  928.       hor = !hor;
  929.     } while (++i < 4);
  930.  
  931.     if (valid) {
  932.       if (fprintf(file, "\n\\put(%.4g,%.4g){\\framebox(%.4g,%.4g){%s%s}}",
  933.                   latex_coord1(double(midx - width / 2 - globbox.min.x)),
  934.                   latex_coord1(double(midy - height / 2 - globbox.min.y)),
  935.                   latex_coord1(double(width)),
  936.                   latex_coord1(double(height)),
  937.                   (label1 == 0 ? "" : label1),
  938.                   (label1 == 0 ? "" : label2)) == 0) return 1;
  939.       label1 = 0; // "label was used"
  940.       return 0;
  941.     }
  942.   }
  943.  
  944.   // is path a circle?
  945.   // 2 to 8 points, 1st and last point same, all beziers
  946.   if (elements >= 4 && elements <= 10
  947.       && element[0].type == move
  948.       && element[1].type == bezier && element[2].type == bezier
  949.       && element[elements - 1].type == close
  950.       && element[elements - 2].type == bezier
  951.       && point[0].near(point[element[elements - 2].firstpoint + 2])) {
  952.     // check remaining points (if any)
  953.     int i;
  954.     XY m(0, 0);
  955.     for (i = 1; i < elements - 1; i++) {
  956.       if (element[i].type != bezier) break;
  957.       m.x += point[element[i].firstpoint + 2].x;
  958.       m.y += point[element[i].firstpoint + 2].y;
  959.     }
  960.     if (i >= elements - 1) {
  961.       m.x /= elements - 2;
  962.       m.y /= elements - 2;
  963.       double minr = 1e10, maxr = 0; // actually radius^2
  964.       double meanr = 0;
  965.       // measure mean radius (and get max and min)
  966.       for (i = 1; i < elements - 1; i++) {
  967.         double dx = double(point[element[i].firstpoint + 2].x - m.x);
  968.         double dy = double(point[element[i].firstpoint + 2].y - m.y);
  969.         double radius = dx * dx + dy * dy;
  970.         log(("Path::latex: circle? Point radius %f\n", radius));
  971.         if (minr > radius)
  972.           minr = radius;
  973.         else if (maxr < radius)
  974.           maxr = radius;
  975.         meanr += radius;
  976.       }
  977.       meanr /= double(elements - 2);
  978.       double r = latex_coord1(sqrt(meanr));
  979.       log(("Path::latex: circle? Mean radius %f pts\n", r));
  980.       if (meanr - minr < meanr / 8 && maxr - meanr < meanr / 8 && r < 40) {
  981.         if (fprintf(file, "\n\\put(%.4g,%.4g){\\circle{%.4g}}",
  982.                     latex_coord1(double(m.x - globbox.min.x)),
  983.                     latex_coord1(double(m.y - globbox.min.y)),
  984.                     r * 2) == 0) return 1; else return 0;
  985.       } else {
  986.         log(("Path::latex: not circlish enough\n"));
  987.       }
  988.     }
  989.   }
  990.  
  991.   // is neither box nor circle
  992.   bool linestart = true;
  993.   for (int i = 0; i < elements; i++) {
  994.     switch (element[i].type) {
  995.     case move:
  996.       linestart = true;
  997.       current = point[element[i].firstpoint];
  998.       current.x -= globbox.min.x; current.y -= globbox.min.y;
  999.       break;
  1000.     case bezier: {
  1001.       linestart = false;
  1002.       XY* points = &point[element[i].firstpoint];
  1003.       XY next = points[2];
  1004.       next.x -= globbox.min.x; next.y -= globbox.min.y;
  1005.       if (points[0].near(points[1])) {
  1006.         XY control;
  1007.         control.avg(points[0], points[1]);
  1008.         control.x -= globbox.min.x; control.y -= globbox.min.y;
  1009.         // control points are identical; use LaTeX's bezier
  1010.         if (fprintf(file, "\n\\qbezier(%.4g,%.4g)(%.4g,%.4g)(%.4g,%.4g)",
  1011.                     latex_coord1(double(current.x)),
  1012.                     latex_coord1(double(current.y)),
  1013.                     latex_coord1(double(control.x)),
  1014.                     latex_coord1(double(control.y)),
  1015.                     latex_coord1(double(next.x)),
  1016.                     latex_coord1(double(next.y))) == 0) return 1;
  1017.       } else {
  1018.         // just draw straight line
  1019.         if (Drawfile::latex_line(file, current.x, current.y, next.x, next.y))
  1020.           return 1;
  1021.       }
  1022.       current = next;
  1023.       break;
  1024.       }
  1025.     case draw: {
  1026.       XY next = point[element[i].firstpoint];
  1027.       next.x -= globbox.min.x; next.y -= globbox.min.y;
  1028.       if (Drawfile::latex_line(file, current.x, current.y, next.x, next.y))
  1029.         return 1;
  1030.       if (linestart && sarrow && Drawfile::latex_arrowhead(file,
  1031.           current.x - Drawfile::latex_lineerrx(),
  1032.           current.y - Drawfile::latex_lineerry(),
  1033.           atan2(current.y - next.y, current.x - next.x))) return 1;
  1034.       linestart = false;
  1035.       // for correct drawfiles, always at least one other tag follows 'draw'
  1036.       if (earrow && i == elements - 1) {
  1037.         if (Drawfile::latex_arrowhead(file,
  1038.             next.x + Drawfile::latex_lineerrx(),
  1039.             next.y + Drawfile::latex_lineerry(),
  1040.             atan2(next.y - current.y, next.x - current.x))) return 1;
  1041.       }
  1042.       current = next;
  1043.       break;
  1044.       }
  1045.     default:
  1046.       linestart = false;
  1047.       break; // do nothing
  1048.     }
  1049.   }
  1050.   return 0;
  1051. }
  1052. //________________________________________
  1053.  
  1054. const char* Label::fontcmd(void) const
  1055. {
  1056.   switch (parent->fontentry(fontnr)) {
  1057.     case roman:
  1058.       return "\\rmfamily ";
  1059.       break;
  1060.     case roman_i:
  1061.       return "\\rmfamily\\itshape ";
  1062.       break;
  1063.     case roman_b:
  1064.       return "\\rmfamily\\bfseries ";
  1065.       break;
  1066.     case roman_ib:
  1067.       return "\\rmfamily\\itshape\\bfseries ";
  1068.       break;
  1069.     case sanss:
  1070.       return "\\sffamily ";
  1071.       break;
  1072.     case sanss_i:
  1073.       return "\\sffamily\\slshape ";
  1074.       break;
  1075.     case sanss_b:
  1076.       return "\\sffamily\\bfseries ";
  1077.       break;
  1078.     case sanss_ib:
  1079.       return "\\sffamily\\slshape\\bfseries ";
  1080.       break;
  1081.     case typew:
  1082.       return "\\ttfamily ";
  1083.       break;
  1084.     case typew_i:
  1085.       return "\\ttfamily\\slshape ";
  1086.       break;
  1087.     case typew_b:
  1088.       return "\\ttfamily\\bfseries ";
  1089.       break;
  1090.     case typew_ib:
  1091.       return "\\ttfamily\\slshape\\bfseries ";
  1092.       break;
  1093.     default:
  1094.       return "";
  1095.       break;
  1096.   }
  1097. }
  1098. //____________________
  1099.  
  1100. int Label::latex(FILE* file, const BBox& globbox) const
  1101. {
  1102.   if (fprintf(file, "\n\\put(%.4g,%.4g){%s%s}",
  1103.               latex_coord1(bbox.min.x - globbox.min.x),
  1104.               latex_coord1(bbox.min.y - globbox.min.y),
  1105.               this->fontcmd(), text) == 0) return 1;
  1106.   return 0;
  1107. }
  1108. //________________________________________
  1109.  
  1110. int Group::latex(FILE* file, const BBox& globbox) const
  1111. {
  1112.   #if 0
  1113.   if (fprintf(file, "\n\\put(%d,%d){\\dashbox{2}(%d,%d){}}",
  1114.               latex_coord(bbox.min.x - globbox.min.x),
  1115.               latex_coord(bbox.min.y - globbox.min.y),
  1116.               latex_coord(bbox.max.x - bbox.min.x),
  1117.               latex_coord(bbox.max.y - bbox.min.y))
  1118.       == 0) return 1;
  1119.   #endif
  1120.  
  1121.   DrawObj* o = first;
  1122.   if (first && (o = first->getnext()) && o->getnext() == 0) {
  1123.     DrawObj* patho = 0;
  1124.     DrawObj* labelo = 0;
  1125.     if (typeid(*first) == typeid(Path)) patho = first;
  1126.     else if (typeid(*o) == typeid(Path)) patho = o;
  1127.     if (typeid(*first) == typeid(Label)) labelo = first;
  1128.     else if (typeid(*o) == typeid(Label)) labelo = o;
  1129.     if (patho && labelo) {
  1130.       thinthick newthinlines = ((Path*)patho)->thinstate();
  1131.       if (print_thinthick(file, newthinlines)) return 1;
  1132.       Path::setlabel(((Label*)labelo)->fontcmd(),
  1133.                        ((Label*)labelo)->getlabel());
  1134.       if (((Path*)patho)->latex(file, globbox)) return 1;
  1135.       if (((Path*)patho)->usedlabel()) return 0;
  1136.       newthinlines = ((Label*)labelo)->thinstate();
  1137.       if (print_thinthick(file, newthinlines)) return 1;
  1138.       if (((Label*)labelo)->latex(file, globbox)) return 1;
  1139.       return 0;
  1140.     }
  1141.   }
  1142.  
  1143.   // output objects
  1144.   o = first;
  1145.   while (o) {
  1146.     thinthick newthinlines = o->thinstate();
  1147.     if (print_thinthick(file, newthinlines)) return 1;
  1148.     if (o->latex(file, globbox)) return 1;
  1149.     o = o->getnext();
  1150.   }
  1151.   return 0;
  1152. }
  1153. //________________________________________
  1154.  
  1155. /* when outputting text area, copy comments (after "\;") verbatim, ignore
  1156.    rest of ASCII source */
  1157. int Textarea::latex(FILE* file, const BBox& globbox) const
  1158. {
  1159.   char* curpos = text;
  1160.   // curpos at the beginning of a new line (text always ends with \n!)
  1161.   while (curpos[1] != 0) {
  1162.     if (*++curpos == '\\' && curpos[1] == ';') {
  1163.       // copy line to output, substituting for "#1" to "#8"
  1164.       if (fputc('\n', file) == EOF) return 1;
  1165.       curpos += 2;
  1166.       do {
  1167.         switch (*curpos) {
  1168.         case '\\':
  1169.           if (*++curpos == '\n') {
  1170.             if (fputc('\\', file) == EOF) return 1;
  1171.           } else {
  1172.             if (fwrite(curpos - 1, 1, 2, file) == 0) return 1;
  1173.             curpos++;
  1174.           }
  1175.           break;
  1176.         case '#':
  1177.           if (*++curpos >= '1' && *curpos <= '8') {
  1178.             // substitute
  1179.             coord val;
  1180.             switch (*curpos++) {
  1181.             case '1': val = bbox.min.x - globbox.min.x; break;
  1182.             case '2': val = bbox.min.y - globbox.min.y; break;
  1183.             case '3': val = bbox.max.x - globbox.min.x; break;
  1184.             case '4': val = bbox.max.y - globbox.min.y; break;
  1185.             case '5': val = bbox.max.x - bbox.min.x; break;
  1186.             case '6': val = bbox.max.y - bbox.min.y; break;
  1187.             case '7':
  1188.               val = (bbox.min.x + bbox.max.x) / 2 - globbox.min.x; break;
  1189.             case '8':
  1190.               val = (bbox.min.y + bbox.max.y) / 2 - globbox.min.y; break;
  1191.             default: val = 0;
  1192.             }
  1193.             if (fprintf(file, "%.4g", latex_coord1(val)) == 0) return 1;
  1194.           } else {
  1195.             if (fputc('#', file) == EOF) return 1;
  1196.           }
  1197.           break;
  1198.         case '%': {
  1199.           // TeX comment - don't substitute for rest of line
  1200.           char* nlpos = curpos;
  1201.           while (*++nlpos != '\n') ;
  1202.           if (fwrite(curpos, 1, nlpos - curpos, file) == 0) return 1;
  1203.           curpos = nlpos;
  1204.           break;
  1205.         }
  1206.         default:
  1207.           if (*curpos != '\n')
  1208.             if (fputc(*curpos++, file) == EOF) return 1;
  1209.         }
  1210.       } while (*curpos != '\n');
  1211.     } else {
  1212.       // skip characters until next \n
  1213.       while (*curpos++ != '\n') ;
  1214.       curpos--;
  1215.     }
  1216.   }
  1217.   return 0;
  1218. }
  1219. //______________________________________________________________________
  1220.  
  1221. static char outname[256]; // name of output file
  1222.  
  1223. int main (int argc, char* argv[])
  1224. {
  1225.   argc--; argv++; // skip first word on command line
  1226.  
  1227.   if (argc == 0) {
  1228.     fprintf(stderr, "Syntax: draw2latex [-shortline] [-origin] <DrawFile> "
  1229.                     "[<DrawFile> ...]\n"
  1230.                     "Output is written to <DrawFile>/tex\n");
  1231.     return 0;
  1232.   }
  1233.  
  1234.   int tmpargc = argc;
  1235.   char** tmpargv = argv;
  1236.   while (tmpargc) {
  1237.     if (strcmp(*tmpargv, "-shortline") == 0 || strcmp(*tmpargv, "-s") == 0) {
  1238.       *tmpargv[0] = '\0';
  1239.       shortbezline = true;
  1240.     } else if (strcmp(*tmpargv, "-origin") == 0
  1241.             || strcmp(*tmpargv, "-o") == 0) {
  1242.       *tmpargv[0] = '\0';
  1243.       usedrawbbox = false;
  1244.     }
  1245.     tmpargc--; tmpargv++;
  1246.   }
  1247.  
  1248.   while (argc) {
  1249.     if (*argv[0] != '\0') {
  1250.       FILE* file = fopen(*argv, "r");
  1251.       if (file == 0) {
  1252.         fprintf(stderr, "Couldn't open \"%s\"\n", *argv);
  1253.       } else {
  1254.         Drawfile* d = new Drawfile;
  1255.         // load each file on command line
  1256.         if (d->load(file))
  1257.           fprintf(stderr, "Couldn't read \"%s\"\n", *argv);
  1258.         fclose(file);
  1259.         sprintf(outname, "%s/tex", *argv);
  1260.         file = fopen(outname, "w");
  1261.         if (file == 0) {
  1262.           fprintf(stderr, "Couldn't open \"%s\"\n", outname);
  1263.         } else {
  1264.           // create global bounding box
  1265.           d->make_bbox();
  1266.           // write converted LaTeX
  1267.           if (d->latex(file))
  1268.             fprintf(stderr, "Couldn't write to \"%s\"\n", outname);
  1269.           fclose(file);
  1270.           _kernel_osfile_block kb;
  1271.           kb.load = 0xaca;
  1272.           _kernel_osfile(18, outname, &kb);
  1273.         }
  1274.         delete d;
  1275.       }
  1276.     }
  1277.     argc--; argv++;
  1278.   }
  1279.  
  1280.   return 0;
  1281. }
  1282.